package com.xquant.pricing.calc.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.xquant.common.engine.xPP.cxf.XQuantPPService;
import com.xquant.common.engine.xPP.cxf.XQuantPPStub;
import com.xquant.common.engine.xPP.cxf.webservice.*;
import com.xquant.common.engine.xPP.request.*;
import com.xquant.common.engine.xPP.response.CalcResult;
import com.xquant.pricing.calc.entity.ProductTypeEnum;
import com.xquant.pricing.calc.entity.RequestCodeEnum;
import com.xquant.pricing.calc.entity.ResponseVo;
import com.xquant.pricing.calc.service.CalcCommonService;
import com.xquant.pricing.calc.service.CalcEngineService;
import com.xquant.pricing.utils.ContextHolder;
import com.xquant.pricing.utils.DateUtils;
import com.xquant.pricing.utils.IRBaseException;
import com.xquant.pricing.utils.SysUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;

@Component
public class CalcEngineServiceImpl implements CalcEngineService, InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(CalcEngineServiceImpl.class);
    @Autowired
    private XQuantPPService ppService;
    @Autowired
    private CalcEngineWebservice calcEngineWebservice;
    @Autowired
    private List<CalcCommonService> calcServiceList;
    private Map<String, Map<String, CalcCommonService>> calcServiceMap = new HashMap<>();

    /**
     * 计算入口 通用
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     */
    @Override
    public ResponseVo calcEngine(String productType, String requestCode, CalcEngineParam param) {
        param.setRequestCode(requestCode);
        // 业务系统的自定义参数是放在定价参数节点中，计算服务的接口是在最外层，需要转换，todo 后续计算服务会放入到calcPricingParam中，到时候，只需删除下面2行代码
        // 存放结果
        ResponseVo ret = new ResponseVo();
        if (param.getCalcPricingParam() == null) return ResponseVo.failure("缺少定价参数calcPricingParam");
        if (ObjectUtil.isNull(param.getCustomPricingParam())) {
            param.setCustomPricingParam(
                    JSON.parse(JSON.toJSONString(param.getCalcPricingParam().getCustomPricingParam())));
        }
        param.getCalcPricingParam().setCustomPricingParam(null);
        // 设置ID
        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        //
        CalcResult result = new CalcResult();
        if (!ProductTypeEnum.STRATEGY.getCode().equals(param.getInstrument().getProductType())) {
            result = doCalcEngineSingle(productType, requestCode, param);
        } else {
            List<CalcEngineParam> params = param.split();
            List<CalcResult> results = new ArrayList<>();
            for (int i = 0; i < params.size(); i++) {
                CalcEngineParam paramItem = params.get(i);

                try {
                    CalcResult resultItem = doCalcEngineSingle(productType, requestCode, paramItem);
                    results.add(resultItem);
                } catch (Exception ex) {
                    logger.warn("组合期权发生计算错误,legId:" + (i + 1));
                }
            }
            result = CalcResult.merge(results);
        }
        ret.setErrorCode(Integer.valueOf(result.getErrorCode()));
        ret.setErrorMsg(result.getErrorMsg());
        ret.setResult(getCalcReuslt(requestCode, result));
        if (Integer.parseInt(result.getErrorCode()) != 0) {
            ret.setErrorMsg("计算出错！" + convertEqErrorMsg(result.getErrorMsg().toLowerCase(Locale.ROOT)));
        }
        return ret;
    }

    /**
     * 计算入口
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     * @desc 根据定价配置ID/默认定价配置 进行计算
     */
    @Override
    public ResponseVo calcEngineByCfgId(String productType, String requestCode, CalcEngineParam param) {
        param.setRequestCode(requestCode);
        // 业务系统的自定义参数是放在定价参数节点中，计算服务的接口是在最外层，需要转换，todo 后续计算服务会放入到calcPricingParam中，到时候，只需删除下面2行代码
        // 存放结果
        ResponseVo ret = new ResponseVo();
        if (param.getCalcPricingParam() == null) return ResponseVo.failure("缺少定价参数calcPricingParam");
        if (ObjectUtil.isNull(param.getCustomPricingParam())) {
            param.setCustomPricingParam(
                    JSON.parse(JSON.toJSONString(param.getCalcPricingParam().getCustomPricingParam())));
        }
        // 设置ID
        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        CalcResult result = new CalcResult();
        if (!ProductTypeEnum.STRATEGY.getCode().equals(param.getInstrument().getProductType())) {
            result = doCalcEngineSingleByCfgId(productType, requestCode, param);
        } else {
            List<CalcEngineParam> params = param.split();
            List<CalcResult> results = new ArrayList<>();
            for (int i = 0; i < params.size(); i++) {
                CalcEngineParam paramItem = params.get(i);
                try {
                    CalcResult resultItem = doCalcEngineSingleByCfgId(productType, requestCode, paramItem);
                    results.add(resultItem);
                } catch (Exception ex) {
                    logger.warn("组合期权发生计算错误,legId:" + (i + 1));
                }
            }
            result = CalcResult.merge(results);
        }
        ret.setErrorCode(Integer.valueOf(result.getErrorCode()));
        ret.setErrorMsg(result.getErrorMsg());
        ret.setResult(getCalcReuslt(requestCode, result));
        if (Integer.parseInt(result.getErrorCode()) != 0) {
            logger.error(ret.getErrorMsg());
            ret.setErrorMsg("计算出错！" + convertEqErrorMsg(result.getErrorMsg().toLowerCase(Locale.ROOT)));
        }
        return ret;
    }

    /**
     * 计算入口
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     */
    private CalcResult doCalcEngineSingleByCfgId(
            String productType, String requestCode, CalcEngineParam param) {
        CalcResult ret = new CalcResult();
        CalcCommonService service;
        ContextHolder.getParam().put("instrumentId", param.getInstrument().getId());
        try {
            service = this.getDelegateService(productType, requestCode);
        } catch (NullPointerException e) {
            e.printStackTrace();
            ret.setErrorCode("-1");
            ret.setErrorMsg("未找到productType对应的业务逻辑");
            return ret;
        }

        String cfgId = service.getDefaultCfgId(param.getCalcPricingParam().getCfgId());
        if (StringUtils.isBlank(cfgId)) {
            ret.setErrorCode("-1");
            ret.setErrorMsg("未传入定价配置，且默认配置不存在");
            return ret;
        }
        /*处理浮动定价参数 将其转换为固定单值*/
        CalcEngineParam calcPricingParamDefault = service.getCalcPricingParamByCfgIdWarpperWithInterpolation(param, cfgId);
        CalcEngineParam engineParamNew = service.mergeCalcPricingParam(calcPricingParamDefault, param);
        param.setCalcPricingParam(engineParamNew.getCalcPricingParam());

//        logger.debug("期权计算条款：" + JSON.toJSONString(param));

        // 引擎平台请求计算服务
        /*CalcResult calcResult = service.beforeCalc(param);
        if (!"0".equals(calcResult.getErrorCode())) {
            return calcResult;
        }*/
        String downReq = JSON.toJSONString(param);

        Map<String, Object> downparam = new WeakHashMap<>();
        downparam.put("downReq", downReq);
        ret = calcEngineWebservice.doCalcEngine(downparam, param);
        logger.debug("期权计算结果：" + JSON.toJSONString(ret));
        service.afterCalc(ret);
        return ret;
    }


    /**
     * 计算入口
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     */
    @Override
    public ResponseVo calcEngineVol(String productType, String requestCode, CalcEngineParam param) {
        param.setRequestCode(requestCode);
        // 业务系统的自定义参数是放在定价参数节点中，计算服务的接口是在最外层，需要转换，todo 后续计算服务会放入到calcPricingParam中，到时候，只需删除下面2行代码
        // 存放结果
        ResponseVo ret = new ResponseVo();
        if (param.getCalcPricingParam() == null) return ResponseVo.failure("缺少定价参数calcPricingParam");
        if (ObjectUtil.isNull(param.getCustomPricingParam())) {
            param.setCustomPricingParam(
                    JSON.parse(JSON.toJSONString(param.getCalcPricingParam().getCustomPricingParam())));
        }
        param.getCalcPricingParam().setCustomPricingParam(null);
        // 设置ID
        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        ret = doCalcEngineVol(productType, requestCode, param);
        return ret;
    }



    /**
     * 计算入口
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     */
    private CalcResult doCalcEngineSingle(
            String productType, String requestCode, CalcEngineParam param) {
        CalcResult ret = new CalcResult();
        CalcCommonService service;
        //20220617 botao.yu 标的价格不能为0
        if (param.getCalcPricingParam() != null && param.getCalcPricingParam().getUnderlyingPricingParams() != null
                && param.getCalcPricingParam().getUnderlyingPricingParams().get(0).getSpotPrice() != null
                && StrUtil.isNotBlank(StrUtil.trim(param.getCalcPricingParam().getUnderlyingPricingParams().get(0).getSpotPrice()))
                && (new BigDecimal(param.getCalcPricingParam().getUnderlyingPricingParams().get(0).getSpotPrice()).compareTo(BigDecimal.ZERO) == 0)) {
            ret.setErrorCode("-1");
            ret.setErrorMsg("标的价格不能为0");
            return ret;
        }
        ContextHolder.getParam().put("instrumentId", param.getInstrument().getId());
        try {
            service = this.getDelegateService(productType, requestCode);
        } catch (NullPointerException e) {
            e.printStackTrace();
            ret.setErrorCode("-1");
            ret.setErrorMsg("未找到productType对应的业务逻辑");
            return ret;
        }
        if (service.isUseDefaultPricingParam(param, requestCode)) {
            String cfgId = service.getDefaultCfgId(param.getCalcPricingParam().getCfgId());
            if (StringUtils.isBlank(cfgId)) {
                ret.setErrorCode("-1");
                ret.setErrorMsg("未传入定价配置，且默认配置不存在");
                return ret;
            }
            CalcEngineParam calcPricingParamDefault = service.getCalcPricingParamByCfgIdWarpperWithInterpolation(param, cfgId);
            CalcEngineParam engineParamNew =
                    service.mergeCalcPricingParam(calcPricingParamDefault, param);
            param.setCalcPricingParam(engineParamNew.getCalcPricingParam());
        }
        logger.debug("期权计算条款：" + JSON.toJSONString(param));

        // 引擎平台请求计算服务
        /*CalcResult calcResult = service.beforeCalc(param);
        if (!"0".equals(calcResult.getErrorCode())) {
            return calcResult;
        }*/
        Map<String, Object> downparam = new WeakHashMap<>();
        ret = calcEngineWebservice.doCalcEngine(downparam, param);
//        logger.debug("期权计算结果：" + JSON.toJSONString(ret));
        service.afterCalc(ret);
        return ret;
    }



    /**
     * 1.有getLegParams，则按leg分别计算，没有的话，普通的计算 2.金融工具拆分 3.找到对应的定价配置 4.循环调用单个
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     */
    private CalcResult doCalcEngine(String productType, String requestCode, CalcEngineParam param) {
        if (!ProductTypeEnum.STRATEGY.getCode().equals(param.getInstrument().getProductType())) {
            return doCalcEngineSingle(productType, requestCode, param);
        }
        List<CalcEngineParam> params = param.split();
        List<CalcResult> results = new ArrayList<>();
        for (int i = 0; i < params.size(); i++) {
            CalcEngineParam paramItem = params.get(i);

            try {
                CalcResult resultItem = doCalcEngineSingle(productType, requestCode, paramItem);
                results.add(resultItem);
            } catch (Exception ex) {
                logger.warn("组合期权发生计算错误,legId:" + (i + 1));
            }
        }
        CalcResult calcResult = CalcResult.merge(results);
        return calcResult;
    }

    /**
     * 1.有getLegParams，则按leg分别计算，没有的话，普通的计算 2.金融工具拆分 3.找到对应的定价配置 4.循环调用单个
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     * @desc 反算
     */
    private ResponseVo doCalcEngineVol(String productType, String requestCode, CalcEngineParam param) {
        if (!ProductTypeEnum.STRATEGY.getCode().equals(param.getInstrument().getProductType())) {
            return doCalcEngineVolSingle(productType, requestCode, param);
        }
        List<CalcEngineParam> params = param.split();
        List<CalcResult> results = new ArrayList<>();
        CalcEngineParam paramItem = params.get(1);
        ResponseVo responseVo = doCalcEngineVolSingle(productType, requestCode, paramItem);

        return responseVo;
    }


    @Override
    public ResponseVo calcVolSurfaceRates(CalcVolSurfaceRatesParam calcVolSurfaceRateParam) {
        ResponseVo resVo = calcVolSurfaceRatesResult(calcVolSurfaceRateParam);
        return resVo;
    }


    /**
     * 计算 期权事件 存续期事件
     *
     * @return
     */
    @Override
    public ResponseVo calcEventInfo(EventInfoParam eventInfoParam) {
        ResponseVo resVo = calcEventInfoResult(eventInfoParam);
        return resVo;
    }

    /**
     * 计算入口 反算波动率
     *
     * @param productType
     * @param requestCode
     * @param param
     * @return
     */
    public ResponseVo doCalcEngineVolSingle(String productType, String requestCode, CalcEngineParam param) {
        ResponseVo ret = new ResponseVo();
        CalcCommonService service;
        ContextHolder.getParam().put("instrumentId", param.getInstrument().getId());
        try {
            service = this.getDelegateService(productType, requestCode);
        } catch (NullPointerException e) {
            e.printStackTrace();
            ret.setErrorCode(-1);
            ret.setErrorMsg("未找到productType对应的业务逻辑");
            return ret;
        }
        if (service.isUseDefaultPricingParam(param, requestCode)) {
            String cfgId = service.getDefaultCfgId(param.getCalcPricingParam().getCfgId());
            if (StringUtils.isBlank(cfgId)) {
                ret.setErrorCode(-1);
                ret.setErrorMsg("未传入定价配置，且默认配置不存在");
                return ret;
            }
            CalcEngineParam calcPricingParamDefault = service.getCalcPricingParamByCfgIdWarpperWithInterpolation(param, cfgId);
            CalcEngineParam engineParamNew =
                    service.mergeCalcPricingParam(calcPricingParamDefault, param);
            param.setCalcPricingParam(engineParamNew.getCalcPricingParam());
        }
        param.convertToBASE64();

        String downReq = JSON.toJSONString(param);

        Map<String, Object> downparam = new WeakHashMap<>();

        downparam.put("downReq", downReq);

        XQuantPPStub stub = null;
        String priority = param.getPriority();
        int retryCount = 0;
        boolean isRetry = true;
        while (isRetry) {
            isRetry = false;
            if (retryCount > 0) {
                priority = "h";
                logger.info("SOAPFaultException导致的重连");
            }
            try {

                CalcEngine pIn = new CalcEngine();
                pIn.setParam(downReq);
                long a = System.nanoTime();
                if ("h".equals(priority)) {
                    stub = ppService.borrowStubPriority();
                } else {
                    stub = ppService.borrowStub();
                }
                long b = System.nanoTime();
                ContextHolder.getParam().put("getStubTime", b - a);
                downparam.put("downReqTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
                ContextHolder.getParam().put("server", stub.getAddress());
                long start = System.nanoTime();
                JSONUtil.toJsonStr(param);
                CalcPricingParam calcPricingParam = param.getCalcPricingParam();
                EquityOptionImplVolParam equityOptionImplVolParam = new EquityOptionImplVolParam();
                equityOptionImplVolParam.setInstrumentID(DateUtils.getCurr_YYYYMMDDHHMMSSSSS() + ",OPT_S,NONE");
                PersonalInsParam insParam = new PersonalInsParam();
                String fpml =
                        Base64.getEncoder()
                                .encodeToString(param.getInstrument().getTermsheet().toString().getBytes("GBK"));

                insParam.setFpML(fpml);
                insParam.setTermSheetType("1");
                equityOptionImplVolParam.setInsParam(insParam);
                equityOptionImplVolParam.setValueDate(calcPricingParam.getValueDate());
                equityOptionImplVolParam.setCalcPPCode(calcPricingParam.getCalcPPCode());
                // 贴现率
                String disCode =
                        calcPricingParam.getDiscount() == null
                                ? null
                                : calcPricingParam.getDiscount().getCode();
                if (StringUtils.isNotBlank(disCode)) {
                    equityOptionImplVolParam.setDscCurveCodeBase64(disCode);
                } else {
                    equityOptionImplVolParam.setDscRate(
                            Double.parseDouble(calcPricingParam.getDiscount().getRate()));
                    // equityOptionImplVolParam.setDscRateType("0");
                    equityOptionImplVolParam.setDayCounterD(calcPricingParam.getDiscount().getDayCount());
                }
                List<UnderlyingPricingParam> list =
                        calcPricingParam.getUnderlyingPricingParams() == null
                                ? null
                                : calcPricingParam.getUnderlyingPricingParams();
                UnderlyingPricingParam u = list.get(0);
                // 红利
                String dividendCode = u.getDividend() == null ? null : u.getDividend().getCode();
                if (StringUtils.isNotBlank(dividendCode)) {
                    u.getDividend().setCode(dividendCode);
                } else {
                    equityOptionImplVolParam.setDivRate(Double.parseDouble(u.getDividend().getRate()));
                    // equityOptionImplVolParam.setDivRateType("0");
                    equityOptionImplVolParam.setDayCounterDiv(u.getDividend().getDayCount());
                }
                // 融资
                String fundingCode = u.getFunding() == null ? null : u.getFunding().getCode();
                if (StringUtils.isNotBlank(fundingCode)) {
                    u.getFunding().setCode(fundingCode);
                } else {
                    equityOptionImplVolParam.setFundingRate(Double.parseDouble(u.getFunding().getRate()));
                    // equityOptionImplVolParam.setFundingRateType("0");
                    equityOptionImplVolParam.setDayCounterF(u.getFunding().getDayCount());
                }
                // 波动率
                String volSurfaceCode = u.getVolSurface() == null ? null : u.getVolSurface().getCode();
                if (StringUtils.isNotBlank(volSurfaceCode)) {
                    u.getVolSurface().setCode(volSurfaceCode);
                } else {
                    equityOptionImplVolParam.setVolAnnualDays("365");
                    equityOptionImplVolParam.setDayCounterV(u.getVolSurface().getDayCount());
                }
                equityOptionImplVolParam.setMarketNPV(calcPricingParam.getMarketNPV());
                equityOptionImplVolParam.setSpotPrice(Double.parseDouble(u.getSpotPrice()));
                JSON.toJSONString(equityOptionImplVolParam);
                EquityOptionImplVolResponse res = stub.calcEquityOptionImplVol(equityOptionImplVolParam);

                long end = System.nanoTime();
                ContextHolder.getParam().put("calcTime", end - start);
                // 计算服务返回结果
                downparam.put("downResp", res.getResult());
                //
                // logger.debug(MessageFormat.format("计算引擎耗时:{0}ns========starTime:{1}====endTime:{2}====server:{3}====downReq===={4}downResp====downResp:{5}",
                //                    end-start,start,end,stub.getAddress(),downReq, res.getResult()));
                downparam.put("downRespTime", DateUtils.getCurrentDateTimeWOnlyMilliS());

                ret.setResult(res.getResult());
                ret.setErrorCode(res.getEquityOptionImplVolResult().getErrorCode());
                ret.setErrorMsg(res.getEquityOptionImplVolResult().getErrorMsg());
            } catch (SOAPFaultException soapFault) {
                soapFault.printStackTrace();
                ret.setErrorCode(-999);
                Boolean isRestart = true;
                if ("Connection refused: connect".equals(soapFault.getMessage())
                        || "Connection timed out".equals(soapFault.getMessage())
                        || "No route to host: connect".equals(soapFault.getMessage())) {
                    retryCount++;
                    isRetry = true;
                } else if ("Connection reset".equals(soapFault.getMessage())) {
                    ret.setErrorMsg("本次试算导致计算服务重启，请检查条款或者参数是否正确");
                    isRestart = false;
                } else {
                    ret.setErrorMsg(soapFault.getMessage());
                }
                if (stub != null && isRestart) {
                    ppService.setTimeOut(stub);
                }
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(soapFault)));
            } catch (WebServiceException ex) {
                ret.setErrorCode(-999);
                ret.setErrorMsg("本次试算导致计算服务重启，请检查条款或者参数是否正确");
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
                ex.printStackTrace();
            } catch (IRBaseException ex) {
                ret.setErrorCode(-999);
                ret.setErrorMsg(ex.getMessage());
                ex.printStackTrace();
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
            } catch (Exception ex) {
                ret.setErrorCode(-999);
                ret.setErrorMsg("未知错误");
                ex.printStackTrace();
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
            } finally {
                if (stub != null) {
                    ppService.returnStub(stub);
                }
                if (ContextHolder.getParam().get("downparams") != null && retryCount == 0) {
                    ((List) ContextHolder.getParam().get("downparams")).add(downparam);
                }
            }
        }
        return ret;
    }

    /**
     * 实际计算入口
     *
     * @param calcVolSurfaceRatesParam
     * @return
     */
    private ResponseVo calcVolSurfaceRatesResult(CalcVolSurfaceRatesParam calcVolSurfaceRatesParam) {
        CalcVolSurfaceRatesResult ret = new CalcVolSurfaceRatesResult();

        String downReq = JSON.toJSONString(calcVolSurfaceRatesParam);
        Map<String, Object> downparam = new WeakHashMap<>();
        downparam.put("downReq", downReq);
        XQuantPPStub stub = null;
        int retryCount = 0;
        boolean isRetry = true;
        ResponseVo resVo = new ResponseVo();
        while (isRetry) {
            isRetry = false;
            if (retryCount > 0) {
                logger.info("SOAPFaultException导致的重连");
            }
            try {
                CalcVolSurfaceRates pIn = new CalcVolSurfaceRates();
                pIn.setParam(calcVolSurfaceRatesParam);
                stub = ppService.borrowStub();
                CalcVolSurfaceRatesResponse res = stub.calcVolSurfaceRates(pIn);
                // 计算服务返回结果
                downparam.put("downResp", res.getResult());
                downparam.put("downRespTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
                if (res.getResult() != null) {
                    resVo.setResult(res.getResult());
                }
                resVo.setErrorCode(0);
            } catch (SOAPFaultException soapFault) {
                soapFault.printStackTrace();
                resVo.setErrorCode(-999);
                Boolean isRestart = true;
                if ("Connection refused: connect".equals(soapFault.getMessage())
                        || "Connection timed out".equals(soapFault.getMessage())
                        || "No route to host: connect".equals(soapFault.getMessage())) {
                    retryCount++;
                    isRetry = true;
                } else if ("Connection reset".equals(soapFault.getMessage())) {
                    resVo.setErrorMsg("本次剥离导致计算服务重启，请检查参数是否正确");
                    isRestart = false;
                } else {
                    resVo.setErrorMsg(soapFault.getMessage());
                }
                if (stub != null && isRestart) {
                    ppService.setTimeOut(stub);
                }
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(soapFault)));
            } catch (WebServiceException ex) {
                resVo.setErrorCode(-999);
                resVo.setErrorMsg("本次剥离导致计算服务重启，请检查参数是否正确");
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
                ex.printStackTrace();
            } catch (IRBaseException ex) {
                resVo.setErrorCode(-999);
                resVo.setErrorMsg(ex.getMessage());
                ex.printStackTrace();
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
            } catch (Exception ex) {
                resVo.setErrorCode(-999);
                resVo.setErrorMsg("未知错误");
                ex.printStackTrace();
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
            } finally {
                if (stub != null) {
                    ppService.returnStub(stub);
                }
                if (ContextHolder.getParam().get("downparams") != null && retryCount == 0) {
                    ((List) ContextHolder.getParam().get("downparams")).add(downparam);
                }
            }
        }
        return resVo;
    }

    /**
     * 实际计算入口
     *
     * @param eventInfoParam
     * @return
     */
    private ResponseVo calcEventInfoResult(EventInfoParam eventInfoParam) {
        CalcEventInfoResponse ret = new CalcEventInfoResponse();

        String downReq = JSON.toJSONString(eventInfoParam);
        Map<String, Object> downparam = new WeakHashMap<>();
        downparam.put("downReq", downReq);
        XQuantPPStub stub = null;
        int retryCount = 0;
        boolean isRetry = true;
        ResponseVo resVo = new ResponseVo();
        while (isRetry) {
            isRetry = false;
            if (retryCount > 0) {
                logger.info("SOAPFaultException导致的重连");
            }
            try {
                stub = ppService.borrowStub();
                CalcEventInfoResponse res = stub.calcEventInfo(eventInfoParam);
                // 计算服务返回结果
                resVo.setErrorCode(res.getCalcEventInfoResult().getErrorCode());
                logger.info(StrUtil.format("getErrorMsg:{}", res.getCalcEventInfoResult().getErrorMsg()));
                resVo.setErrorMsg(res.getCalcEventInfoResult().getErrorMsg());
                downparam.put("downResp", res.getResult());
                downparam.put("downRespTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
                if (res.getResult() != null) {
                    resVo.setErrorCode(0);
                    resVo.setResult(res.getResult());
                }
            } catch (SOAPFaultException soapFault) {
                soapFault.printStackTrace();
                resVo.setErrorCode(-999);
                Boolean isRestart = true;
                if ("Connection refused: connect".equals(soapFault.getMessage())
                        || "Connection timed out".equals(soapFault.getMessage())
                        || "No route to host: connect".equals(soapFault.getMessage())) {
                    retryCount++;
                    isRetry = true;
                } else if ("Connection reset".equals(soapFault.getMessage())) {
                    resVo.setErrorMsg("本次剥离导致计算服务重启，请检查参数是否正确");
                    isRestart = false;
                } else {
                    resVo.setErrorMsg(soapFault.getMessage());
                }
                if (stub != null && isRestart) {
                    ppService.setTimeOut(stub);
                }
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(soapFault)));
            } catch (WebServiceException ex) {
                resVo.setErrorCode(-999);
                resVo.setErrorMsg("本次剥离导致计算服务重启，请检查参数是否正确");
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
                ex.printStackTrace();
            } catch (IRBaseException ex) {
                resVo.setErrorCode(-999);
                resVo.setErrorMsg(ex.getMessage());
                ex.printStackTrace();
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
            } catch (Exception ex) {
                resVo.setErrorCode(-999);
                resVo.setErrorMsg("未知错误");
                ex.printStackTrace();
                logger.error(MessageFormat.format("====error:{0}", SysUtils.getErrorMsg(ex)));
            } finally {
                if (stub != null) {
                    ppService.returnStub(stub);
                }
                if (ContextHolder.getParam().get("downparams") != null && retryCount == 0) {
                    ((List) ContextHolder.getParam().get("downparams")).add(downparam);
                }
            }
        }
        return resVo;
    }

    @Override
    public CalcCommonService getDelegateService(String productType, String requestCode) {
        return calcServiceMap.get(productType).get(requestCode);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        for (CalcCommonService service : calcServiceList) {
            String[] productTypes = service.getSupportedProductType();
            String requestCode = service.getSupportedRequestCode();
            for (String productType : productTypes) {
                if (!calcServiceMap.containsKey(productType)) {
                    calcServiceMap.put(productType, new HashMap<>());
                }
                calcServiceMap.get(productType).put(requestCode, service);
            }
        }
    }

    public final List<String> ppErrorEnum = Arrays.asList("解析XML Data", "获取观察价格失败", "行权价为空","找不到插件配置", "波动率曲面插值行权价为空", "无效的行权价格", "无效的远期值", "无效的波动率值", "无效的标的数量", "无效的中间值TV", "普通欧式期权不支持BS、BSAnalytical以外的定价模式");

    /*计算错误处理*/
    private String convertEqErrorMsg(String ppErrorMsg) {

        if (ppErrorMsg.contains("errormessage")) {
            String errorM = ppErrorMsg.split("errormessage")[1];
            for (String item : ppErrorEnum) {
                if (errorM.contains(item)) {
                    return item;
                }
            }
            return errorM;
        } else {
            if (ppErrorMsg.contains("px!=")) {
                return "条款解析异常";
            }
            return ppErrorMsg;
        }
    }

    private Object getCalcReuslt(String requestCode, CalcResult result) {
        if (RequestCodeEnum.CALC_MARKET.getCode().equals(requestCode)) {
            return result.getCalcMarket();
        } else if (RequestCodeEnum.CALC_SCENE.getCode().equals(requestCode)) {
            return result.getCalcScene();
        } else if (RequestCodeEnum.CALC_PAYMENTINFO.getCode().equals(requestCode)) {
            return result.getCalcPaymentInfo();
        } else if (RequestCodeEnum.READ_FIXINGDATES.getCode().equals(requestCode)) {
            return result.getReadFixingDates();
        } else if (RequestCodeEnum.CALC_ACCRUALDETAIL.getCode().equals(requestCode)) {
            return result.getCalcAccrualDetail();
        } else if (RequestCodeEnum.CALC_ASSETDETAIL.getCode().equals(requestCode)) {
            return result.getCalcAssetDetail();
        } else if (RequestCodeEnum.CALC_MATURITY_PAYMENT.getCode().equals(requestCode)) {
            return result.getCalcMaturityPayment();
        } else if (RequestCodeEnum.CALC_TRIAL.getCode().equals(requestCode)) {
            return result.getCalcTrial();
        } else {
            return null;
        }
    }
}
